#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;

namespace Lombiq.HelpfulLibraries.Common.Utilities;

/// <summary>
/// 根据指定的版本部分将 <see cref="Version"/> 实例分组为树结构，以便可以使用索引按数字选择它们。
/// </summary>
/// <param name="Versions">当前子树中的所有版本。</param>
/// <param name="SubVersions">按直接下一个版本部分分组的版本。</param>
public record VersionTree(IReadOnlyList<Version> Versions, IReadOnlyDictionary<int, VersionTree> SubVersions)
{
    /// <summary>
    /// 获取提供的键的子树，如果存在的话（否则返回 <see langword="null"/>）。如果 <paramref name="index"/> 为负数，则返回此实例。
    /// </summary>
    public VersionTree? this[int index]
    {
        get
        {
            if (index < 0) return this;
            return SubVersions.TryGetValue(index, out var sub) ? sub : null;
        }
    }

    /// <summary>
    /// 获取与提供的 <paramref name="version"/> 匹配的所有版本，该版本可以是部分的/浮动的。
    /// </summary>
    public VersionTree? this[Version? version] =>
        version is null
            ? this
            : this[version.Major]?[version.Minor]?[version.Build]?[version.Revision];

    /// <summary>
    /// 从提供的 <paramref name="versions"/> 的副本创建新树。
    /// </summary>
    public static VersionTree Create(IEnumerable<Version> versions)
    {
        var allVersions = versions.ToList();

        var majorVersions =
            FromVersions(allVersions, version => version.Major, major =>
                FromVersions(major, version => version.Minor, minor =>
                    FromVersions(minor, version => version.Build, revision =>
                        FromVersions(revision, version => version.Revision, _ => []))));

        return new(allVersions, majorVersions);
    }

    private static Dictionary<int, VersionTree> FromVersions(
        IEnumerable<Version> versions,
        Func<Version, int> selector,
        Func<IEnumerable<Version>, Dictionary<int, VersionTree>> subSelector) =>
        versions
            .Where(version => selector(version) >= 0)
            .GroupBy(selector)
            .ToDictionary(
                group => group.Key,
                items => new VersionTree([.. items], subSelector(items)));
}
